defmodule BlockingQueue do
  use GenServer

  @empty_queue :queue.new()

  def start_link(options) do
    GenServer.start_link(__MODULE__, nil, options)
  end

  def push(bq, item) do
    GenServer.call(bq, {:push, item})
  end

  def pop(bq) do
    GenServer.call(bq, :pop, :infinity)
  end


  @impl true
  def init(_) do
    {:ok, {@empty_queue, @empty_queue}}
  end

  @impl true
  def handle_call({:push, item}, _from, {items, refs}) do
    {:reply, :ok, {:queue.in(item, items), refs}, {:continue, :pop}}
  end

  @impl true
  def handle_call(:pop, from, {items, refs}) do
    {:noreply, {items, :queue.in(from, refs)}, {:continue, :pop}}
  end

  @impl true
  def handle_continue(:pop, {@empty_queue, _refs} = state) do
    {:noreply, state}
  end

  def handle_continue(:pop, {_items, @empty_queue} = state) do
    {:noreply, state}
  end

  def handle_continue(:pop, {items, refs}) do
    {{:value, item}, items} = :queue.out(items)
    {{:value, ref}, refs} = :queue.out(refs)
    GenServer.reply(ref, item)
    {:noreply, {items, refs}, {:continue, :pop}}
  end
end
